/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mabi.frontend;

import com.mabi.modular.runtime.application.IFrontEndApp;
import com.mabi.frontend.appbuilder.ApplicationConfigurer;
import com.mabi.frontend.appbuilder.BasicGuiBuilder;
import com.mabi.modular.runtime.module.RuntimeMabiModule;
import com.mabi.modular.runtime.module.contributions.toolbars.Toolbar;
import com.mabi.modular.runtime.module.contributions.views.ViewImpl;
import com.mabi.modular.runtime.module.contributions.zones.Zone;
import com.mabi.modular.runtime.module.contributions.toolbars.ToolbarItem;
import com.mabi.frontend.appbuilder.MabiAppBuilder;
import com.mabi.frontend.appbuilder.modulemanager.ModuleManager;
import com.mabi.modular.runtime.module.contributions.actions.AbstractMabiAction;
import com.mabi.modular.runtime.module.contributions.menus.Menu;
import com.vaadin.Application;
import com.vaadin.terminal.ParameterHandler;
import com.vaadin.ui.AbstractComponentContainer;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Accordion;
import com.vaadin.ui.Component;
import com.vaadin.ui.Component.Listener;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.SplitPanel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TabSheet.Tab;
import com.vaadin.ui.Window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.SessionScoped;

/**
 *
 * @author User
 */
@SessionScoped
public class FrontEndApp extends Application implements IFrontEndApp {

    int count = 0;
    private MabiAppBuilder appBuilder;
    private Map<Integer, RuntimeMabiModule> loadedModules;
    private Map<String, Object> appContext;
    
    @Override
    public void init() {
        appBuilder = new MabiAppBuilder(this);
        Window appMainWindow;
        appMainWindow = new Window();
        appMainWindow.addComponent(new Label("Error inicializando aplicación"));
        setMainWindow(appMainWindow);
        try {
            //Configures application
            ApplicationConfigurer.configure(this);
        } catch (Exception e) {
            appMainWindow.showNotification("Error inicializando aplicaciónX", "<br>Error: " + e.getMessage(), Window.Notification.TYPE_ERROR_MESSAGE);
        }
        try {
            appBuilder.buildMainAppWindow(new BasicGuiBuilder());
            appBuilder.startUpFramework();
            ModuleManager.getDefault(this).loadModules();
        } catch (Exception e) {
            appMainWindow.showNotification("Error inicializando aplicación", "<br>Error: " + e.getMessage(), Window.Notification.TYPE_ERROR_MESSAGE);
        }
    }
    //////////////
    List<ViewImpl> views;
    List<MenuItem> menuItems;
    TabSheet centerViewContainer = null;

    //<editor-fold defaultstate="collapsed" desc="Zones">
    @Override
    public void addZone(String legalZoneName) {
    }

    @Override
    public void addZone(Zone zone, String parentZone, boolean showHead) {
        //Do some configurations
        final String[] legalZoneNames = {FrontEndApp.WEST_SUB_ZONE};

        //Checks if the zone name is valid
        if (Arrays.asList(legalZoneNames).contains(zone)) {
            System.out.println("Proceed");
        } else {
            System.out.println("NO WAY");
        }

    }

    @Override
    public void removeZone(Zone zone) {
    }

    @Override
    public void moveZone(Zone zone, Zone newParentZone) {
    }

    @Override
    public void moveAllZoneContent(Zone zone, Zone destinationZone) {
    }

    @Override
    public void detachZone(Zone zone) {
    }

    @Override
    public void attachZone(Zone zone, Zone parentZone, int position) {
    }

    private boolean zoneExists(String zoneName) {
        Component locateZone = locateZone(zoneName, null);
        if (locateZone != null) {
            return true;
        } else {
            return false;
        }
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Views">
    @Override
    public void addView(ViewImpl view) throws Exception {
        addView(view, null);
    }

    @Override
    public void addView(ViewImpl view, String zoneName) throws Exception {

        //Checks if the view is already added
        if (viewExists(view)) {
            throw new Exception("View is already added. Same instances of views should not coexist");
        }

        if (view.getModuleName().equals("")) {
            throw new Exception("View.moduleClass is mandatory");
        }

        //Determines the zone into which the view will be rendered
        String targetZoneName = "";
        if (null != zoneName) {
            targetZoneName = zoneName;
        } else if (!view.getZoneName().isEmpty()) {
            targetZoneName = view.getZoneName();
        } else {
            targetZoneName = FrontEndApp.CENTER_SUB_ZONE;
        }

        if (!zoneExists(targetZoneName)) {
            this.addZone(null, targetZoneName, true);
        }

        //Sets the view instance zone to the zone determined here
        view.setZoneName(targetZoneName);

        TabSheet viewContainer = null;//The view container inside the zone

        //Locates the target zone
        Zone targetZone = (Zone) locateZone(targetZoneName, null);

        if (targetZone == null) {//Since the target zone is null, creating it will be attempted
            throw new Exception("Zone could not be found or created");
        }

        viewContainer = getViewContainer(targetZoneName);

        //In case that no container is found, it attempts to create the view container component when no container have been found
        if (viewContainer == null) {//A view container needs to be created in the zone
            //For center zone the container is a TabSheet
            if (targetZoneName.equals(FrontEndApp.CENTER_SUB_ZONE)) {//The center zone holds views in the viewTabComponent
                viewContainer = new TabSheet();
                viewContainer.setSizeFull();
                centerViewContainer = viewContainer;
            } else {//Other but center zone have other strategies to harbor views
                viewContainer = new Accordion();
                viewContainer.setSizeFull();
            }
            viewContainer.setDebugId(targetZoneName + "ViewContainer");
            //Put the views container in the zone
            targetZone.addComponent(viewContainer);
        }

        //Insert the view into the view container
        Tab viewTab = viewContainer.addTab(view);

        //Configure the view tab
        viewTab.setDescription(view.getDescription());
        viewTab.setClosable(view.isClosable());
        viewTab.setEnabled(view.isEnabled());
        viewTab.setIcon(view.getIcon());

        //Adds the view to the view collection
        if (null == views) {
            views = new ArrayList();
        }
        views.add(view);
    }

    @Override
    public void removeView(ViewImpl view) throws Exception {
        String zoneName = view.getZoneName();
        TabSheet viewContainer = getViewContainer(zoneName);
        if (!views.contains(view)) {
            throw new Exception("View not found");
            //return;
        }
        try {
            viewContainer.removeComponent(view);
            views.remove(view);
        } catch (Exception e) {
            throw e;
        }
    }

    private Component findView(String viewCaption) {
        Iterator<Component> tabs = centerViewContainer.getComponentIterator();
        Component componentToRemove = null;
        while (tabs.hasNext()) {
            Component component = tabs.next();
            if (component.getCaption().equals(viewCaption)) {
                componentToRemove = component;
                break;
            }
        }
        return componentToRemove;
    }

    @Override
    public void remarkView(ViewImpl view, String remakText, int timeout) {
    }

    @Override
    public void moveView() {
    }

    @Override
    public void jumpView(String viewCaption, int position) {
    }

    @Override
    public void selectView(String viewCaption) {
        Component viewToSelect = findView(viewCaption);
        centerViewContainer.setSelectedTab(viewToSelect);
    }

    @Override
    public void selectSubView(String viewCaption, String subViewCaption) {
    }

    @Override
    public TabSheet getViewContainer(String viewContainerZoneName) throws Exception {
        Zone targetZone = (Zone) locateZone(viewContainerZoneName, null);

        if (targetZone == null) {//Since the target zone is null, creating it will be attempted
            throw new Exception("Zone could not be found");
        }

        //Extract the components inside the zone to know where to harbor the view
        Iterator<Component> targetZoneComponentsIterator = targetZone.getComponentIterator();

        int containerCount = 0;
        TabSheet viewContainer = null;//The view container inside the zone

        //Get existing view containers
        while (targetZoneComponentsIterator.hasNext()) {
            containerCount++;
            if (containerCount == 1) {//Only the first one found is needed since zones should not have more than one
                viewContainer = (TabSheet) targetZoneComponentsIterator.next();
                break;//Thus break;
            }
        }
        targetZoneComponentsIterator = null;//Clean the iterator
        return viewContainer;
    }

    private boolean viewExists(ViewImpl view) {
        boolean result = false;
        if (views == null) {
            result = false;
        } else {
            if (views.contains(view)) {
                result = true;
            } else {
                result = false;
            }
        }
        return result;
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Toolbar">
    @Override
    public void addToolbar(Toolbar toolbar) {
    }

    @Override
    public void removeToolbar(Toolbar toolbar) {
    }

    @Override
    public void remarkToolbar(Toolbar toolbar, String bubbleText, int timeout) {
    }

    @Override
    public void addToolbarItem(ToolbarItem toolbarItem, int position) {
    }

    @Override
    public void disableToolbarItem(ToolbarItem toolbarItem) {
    }

    @Override
    public void enableToolbarItem(ToolbarItem toolbarItem) {
    }

    @Override
    public void replaceToolbarItem(ToolbarItem oldToolbarItem, ToolbarItem newToolbarItem) {
    }

    @Override
    public void executeToolbarItem(ToolbarItem toolbarItem) {
    }

    @Override
    public void removeToolbarItem(ToolbarItem toolbarItem) {
    }

    @Override
    public void moveToolbarItem(ToolbarItem toolbarItem, int newPosition, Toolbar toolbar) {
    }

    //<editor-fold defaultstate="collapsed" desc="System tools toolbar">
    @Override
    public void addSystemToolsComponent(Component component, int position) {
    }

    @Override
    public void removeSystemToolsComponent(Component component) {
    }

    @Override
    public void remarkSystemToolsComponent(Component component, String remarkText, int timeout) {
    }

    @Override
    public void disableSystemToolsComponent(Component component, String disabledText) {
    }

    @Override
    public void enableSystemToolsComponent(Component component) {
    }
    //</editor-fold>

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Menu">
    @Override
    public void addMenuBar(Menu menu) {
    }

    @Override
    public void removeMenu(Menu menu) {
    }

    @Override
    public void remarkMenuItem(MenuItem menuItem, String bubbleText, int timeout) {
    }

    /**
     * Adds a menu item under the parent menu item. If parent is null, the item
     * will be added in the menu bar.
     * @param menuItem
     * @param parentItem 
     */
    @Override
    public void addMenuItem(MenuItem menuItem, int position, MenuItem parentItem) {
    }

    @Override
    public void removeMenuItem(MenuItem menuItem) {
    }

    @Override
    public void disableMenuItem(MenuItem menuItem, String description) {
    }

    @Override
    public void enableMenuItem(MenuItem menuItem) {
    }

    @Override
    public void replaceMenuItem(MenuItem oldMenuItem, MenuItem newMenuItem) {
    }

    @Override
    public void executeMenuItem(MenuItem menuItem) {
    }

    @Override
    public void addSystemMenuItem(MenuItem menuItem) {
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Application">
    @Override
    public void setAppCaption(String caption) {
    }

    @Override
    public void addParameterProcessor(ParameterHandler handler) {
    }

    @Override
    public void showSplash(int timeOut) {
    }

    @Override
    public void blockApplication(String message, boolean desktopVisible) {
    }

    @Override
    public void unBlockApplication() {
    }

    @Override
    public Map<String, Object> getAppContext() {
        return appContext;
    }

    @Override
    public synchronized void setAppContext(Map<String, Object> appContext) {
        this.appContext = appContext;
    }

    @Override
    public synchronized void setContextItem(String itemName, Object itemValue) {
        this.getAppContext().put(itemName, itemValue);
    }

    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Status bar">
    @Override
    public void setStatusText(String statusBarText) {
    }

    @Override
    public void addStatusBarComponent(Component component, String statusBarZone, int position) {
    }

    @Override
    public void removeStatusBarComponent(Component component) {
    }

    //</editor-fold>
    @Override
    public void executeAction(AbstractMabiAction action) {
    }

    /**
     * Presents a progress bar on the status bar for finite running processes
     * @param label
     * @param stopListener
     * @param status 
     */
    @Override
    public void runBackGroundProcess(String label, Listener stopListener, int status) {
    }

    /**
     * Presents a progress bar on the status bar for undefined running processes
     * @param label
     * @param stopListener 
     */
    @Override
    public void runBackGroundProcess(String label, Listener stopListener) {
    }

    /**
     * Locates a zone inside the given zone container.
     * If none specified, then the Main Window is assumed.
     * @param debugId
     * @param insideOf
     * @return 
     */
    private Component locateZone(String debugId, AbstractComponentContainer insideOf) {

        Component componentFound = null;

        if (insideOf == null) {
            insideOf = getMainWindow();
        }
        Iterator<Component> comps = insideOf.getComponentIterator();
        while (comps.hasNext()) {
            Component nextComponent = comps.next();
            String lDebugId = (nextComponent.getDebugId() == null) ? "N/A" : nextComponent.getDebugId();

            if (lDebugId.equals(debugId)) {
                componentFound = nextComponent;
                break;
            } else {
                //Class<?>[] interfaces = nextComponent.getClass().getInterfaces();
                String superClassName = nextComponent.getClass().getSuperclass().getName();

                List<String> searchFor = Arrays.asList(new String[]{"com.vaadin.ui.AbstractComponentContainer", "com.vaadin.ui.AbstractLayout", "com.vaadin.ui.Panel", "com.vaadin.ui.CustomLayout", Zone.class.getName(), SplitPanel.class.getName(), AbstractOrderedLayout.class.getName()});
                String classOfContainer = nextComponent.getClass().getSuperclass().getName();
                boolean contains = searchFor.contains(classOfContainer);

                if (searchFor.contains(classOfContainer)) {
                    componentFound = locateZone(debugId, (AbstractComponentContainer) nextComponent);
                    if (componentFound != null) {
                        break;
                    }
                }
                if (componentFound != null) {
                    break;
                }
            }
        }
        return componentFound;
    }

    //<editor-fold defaultstate="collapsed" desc="Modules">
    @Override
    public RuntimeMabiModule getLoadedModule(String moduleName) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public List<RuntimeMabiModule> getLoadedModules() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void loadModule(RuntimeMabiModule module) {
        if (null == loadedModules) {
            loadedModules = new TreeMap();
        }
        Integer modulesCurrentCount = loadedModules.size();
        loadedModules.put(modulesCurrentCount, module);
        List<ViewImpl> autoloadViews = module.getAutoloadViews();

        for (ViewImpl view : autoloadViews) {
            try {
                addView(view);
            } catch (Exception ex) {
                Logger.getLogger(FrontEndApp.class.getName()).log(Level.SEVERE, null, ex);
            }
        }


    }

    @Override
    public void unLoadModule(RuntimeMabiModule module) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
